package com.springsecurity.services;

import com.springsecurity.data.constants.ObjectModelConstants;
import com.springsecurity.data.domains.Attribute;
import com.springsecurity.data.domains.AttributeObjectType;
import com.springsecurity.data.domains.DBObject;
import com.springsecurity.data.domains.Parameter;
import com.springsecurity.data.repositories.AttributeObjectTypeRepository;
import com.springsecurity.data.repositories.AttributeRepository;
import com.springsecurity.data.repositories.DBObjectRepository;
import com.springsecurity.data.repositories.ParameterRepository;
import com.springsecurity.model.common.ModelObject;
import com.springsecurity.model.exceptions.IncorrectAttributeTypeException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 *
 * @author evob0413
 */
@Service
public class ModelObjectsService {

    @Autowired
    DBObjectRepository objectRepository;

    @Autowired
    AttributeRepository attributeRepository;

    @Autowired
    AttributeObjectTypeRepository attributeObjectTypeRepository;

    @Autowired
    ParameterRepository parameterRepository;

    public ModelObject getObjectByObjectId(int objectId) {
        DBObject object = objectRepository.findOne(objectId);
        Map<Integer, Object> params = getObjectParametersByObjectIdAndAttributes(
                objectId, getAttributesByObjectType(object.getObjectTypeId()));
        return new ModelObject(objectId, object.getName(), object.getDescription(),
                object.getObjectTypeId(), params);
    }

    public List<ModelObject> getObjectByObjectTypeAndName(int objectTypeId,
            String name) {
        List<ModelObject> modelObjects = new ArrayList<>();
        List<DBObject> objects = objectRepository.findByObjectTypeIdAndName(objectTypeId, name);
        for (DBObject object : objects) {
            Map<Integer, Object> params = getObjectParametersByObjectIdAndAttributes(object
                    .getObjectId(), getAttributesByObjectType(objectTypeId));
            modelObjects.add(new ModelObject(object.getObjectId(), object.getName(),
                    object.getDescription(), objectTypeId, params));
        }
        return modelObjects;
    }

    private List<Attribute> getAttributesByObjectType(int objectTypeId) {
        List<AttributeObjectType> aots = attributeObjectTypeRepository.findByObjectTypeId(objectTypeId);
        List<Attribute> attrs = new ArrayList<>();
        for (AttributeObjectType aot : aots) {
            Attribute attr = attributeRepository.findOne(aot.getAttributeId());
            if (attr != null) {
                attrs.add(attr);
            }
        }
        return attrs;
    }

    private Map<Integer, Object> getObjectParametersByObjectIdAndAttributes(
            int objectId, List<Attribute> attributes) {
        Map<Integer, Object> parameters = new HashMap<>();
        if (attributes == null) {
            return parameters;
        }
        for (Attribute attr : attributes) {
            Parameter param = parameterRepository.findByObjectIdAndAttributeId(objectId, ((Attribute) attr).getAttributeId());
            if (param != null) {
                try {
                    Object value = getParameterValue(param, attr.getAttributeTypeId());
                    if (value != null) {
                        parameters.put(param.getAttributeId(), value);
                    }
                } catch (IncorrectAttributeTypeException e) {
                    //TODO
                    throw new RuntimeException(e);
                }
            }
        }
        return parameters;
    }

    private Object getParameterValue(Parameter param, int attributeType)
            throws IncorrectAttributeTypeException {
        switch (attributeType) {
            case ObjectModelConstants.TYPE_TEXT:
                return param.getTextValue();
            case ObjectModelConstants.TYPE_NUMBER:
                return param.getNumberValue();
            case ObjectModelConstants.TYPE_BOOLEAN:
                return param.getBooleanValue();
            case ObjectModelConstants.TYPE_DATE:
                return param.getDateValue();
            case ObjectModelConstants.TYPE_BINARY:
                return param.getBinaryValue();
            case ObjectModelConstants.TYPE_REFERENCE:
                return param.getReferenceValue();
            default:
                throw new IncorrectAttributeTypeException("Attribute Type "
                        + attributeType + " is incorrect");
        }
    }
}
